// ============================================
// TypeScript 装饰器示例
// 注意：需要在 tsconfig.json 中启用 "experimentalDecorators": true
// ============================================

export {}

import "reflect-metadata";
// 1. 类装饰器 (Class Decorators)
function sealed(constructor: Function) {
    Object.seal(constructor);
    Object.seal(constructor.prototype);
}

function classDecorator<T extends { new(...args: any[]): {} }>(constructor: T) {
    return class extends constructor {
        newProperty = "new property";
        hello = "override";
    };
}

@sealed
@classDecorator
class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

let greeter = new Greeter("world");
console.log(greeter.greet());
console.log((greeter as any).newProperty);

// 2. 方法装饰器 (Method Decorators)
function enumerable(value: boolean) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        descriptor.enumerable = value;
    };
}

function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    
    descriptor.value = function (...args: any[]) {
        console.log(`Calling ${propertyKey} with arguments:`, args);
        const result = originalMethod.apply(this, args);
        console.log(`${propertyKey} returned:`, result);
        return result;
    };
    
    return descriptor;
}

class Calculator {
    @enumerable(false)
    @log
    add(a: number, b: number): number {
        return a + b;
    }
    
    @log
    multiply(a: number, b: number): number {
        return a * b;
    }
}

let calc = new Calculator();
calc.add(2, 3);
calc.multiply(4, 5);

// 3. 访问器装饰器 (Accessor Decorators)
function configurable(value: boolean) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        descriptor.configurable = value;
    };
}

class Point {
    private _x: number;
    private _y: number;
    
    constructor(x: number, y: number) {
        this._x = x;
        this._y = y;
    }
    
    @configurable(false)
    get x() {
        return this._x;
    }
    
    @configurable(false)
    get y() {
        return this._y;
    }
}

// 4. 属性装饰器 (Property Decorators)
function format(formatString: string) {
    return function (target: any, propertyKey: string) {
        let value = target[propertyKey];
        
        const getter = function () {
            return value;
        };
        
        const setter = function (newVal: string) {
            value = formatString.replace('%s', newVal);
        };
        
        Object.defineProperty(target, propertyKey, {
            get: getter,
            set: setter,
            enumerable: true,
            configurable: true
        });
    };
}

function validate(target: any, propertyKey: string) {
    let value = target[propertyKey];
    
    const getter = function () {
        return value;
    };
    
    const setter = function (newVal: any) {
        if (typeof newVal !== 'string' || newVal.length < 3) {
            throw new Error(`${propertyKey} must be a string with at least 3 characters`);
        }
        value = newVal;
    };
    
    Object.defineProperty(target, propertyKey, {
        get: getter,
        set: setter,
        enumerable: true,
        configurable: true
    });
}

class User {
    @format("Hello, %s")
    name: string;
    
    @validate
    email: string;
    
    constructor(name: string, email: string) {
        this.name = name;
        this.email = email;
    }
}

let user = new User("John", "john@example.com");
console.log(user.name); // "Hello, John"

// 5. 参数装饰器 (Parameter Decorators)
function required(target: any, propertyKey: string | symbol, parameterIndex: number) {
    let existingRequiredParameters: number[] = Reflect.getOwnMetadata("required", target, propertyKey) || [];
    existingRequiredParameters.push(parameterIndex);
    Reflect.defineMetadata("required", existingRequiredParameters, target, propertyKey);
}

function validate2(target: any, propertyName: string, descriptor: TypedPropertyDescriptor<(...args: any[]) => any>) {
    let method = descriptor.value!;
    
    descriptor.value = function (...args: any[]) {
        let requiredParameters: number[] = Reflect.getOwnMetadata("required", target, propertyName);
        if (requiredParameters) {
            for (let parameterIndex of requiredParameters) {
                if (parameterIndex >= args.length || args[parameterIndex] === undefined) {
                    throw new Error("Missing required argument.");
                }
            }
        }
        return method.apply(this, args);
    };
}

class BankAccount {
    private _balance: number = 0;
    
    @validate2
    deposit(@required amount: number) {
        this._balance += amount;
        return this._balance;
    }
    
    get balance() {
        return this._balance;
    }
}

// 注意：需要安装 reflect-metadata 包才能使用 Reflect.getOwnMetadata
// npm install reflect-metadata
// import "reflect-metadata";

// 6. 装饰器工厂 (Decorator Factories)
function Component(options: { selector: string; template: string }) {
    return function <T extends { new(...args: any[]): {} }>(constructor: T) {
        return class extends constructor {
            selector = options.selector;
            template = options.template;
        };
    };
}

function Injectable() {
    return function <T extends { new(...args: any[]): {} }>(constructor: T) {
        // 标记类为可注入
        (constructor as any).__injectable__ = true;
        return constructor;
    };
}

@Component({
    selector: 'app-hello',
    template: '<h1>Hello World</h1>'
})
@Injectable()
class HelloComponent {
    message = "Hello from component!";
}

let component = new HelloComponent();
console.log((component as any).selector);
console.log((component as any).template);

// 7. 多个装饰器的执行顺序
function first() {
    console.log("first(): factory evaluated");
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log("first(): called");
    };
}

function second() {
    console.log("second(): factory evaluated");
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log("second(): called");
    };
}

class ExampleClass {
    @first()
    @second()
    method() {
        console.log("method called");
    }
}

// 输出顺序：
// first(): factory evaluated
// second(): factory evaluated
// second(): called
// first(): called

// 8. 实际应用示例：简单的依赖注入
const serviceRegistry = new Map<string, any>();

function Service(name: string) {
    return function <T extends { new(...args: any[]): {} }>(constructor: T) {
        serviceRegistry.set(name, constructor);
        return constructor;
    };
}

function Inject(serviceName: string) {
    return function (target: any, propertyKey: string) {
        Object.defineProperty(target, propertyKey, {
            get: () => {
                const ServiceClass = serviceRegistry.get(serviceName);
                return ServiceClass ? new ServiceClass() : null;
            },
            enumerable: true,
            configurable: true
        });
    };
}

@Service('logger')
class Logger {
    log(message: string) {
        console.log(`[LOG]: ${message}`);
    }
}

@Service('userService')
class UserService {
    @Inject('logger')
    private logger!: Logger;
    
    getUser(id: number) {
        this.logger.log(`Getting user with id: ${id}`);
        return { id, name: `User ${id}` };
    }
}

let userService = new UserService();
userService.getUser(1);

console.log("=== 装饰器示例完成 ===");